﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Drawing;

namespace System.Windows.Forms.Design;

/// <summary>
///  Provides a base implementation for a <see cref="ComponentEditorPage"/>.
/// </summary>
public abstract class ComponentEditorPage : Panel
{
    private Icon? _icon;

    /// <summary>
    ///  Initializes a new instance of the <see cref="ComponentEditorPage"/> class.
    /// </summary>
    public ComponentEditorPage() : base()
    {
        Visible = false;
    }

    /// <summary>
    ///  Hide the property
    /// </summary>
    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override bool AutoSize
    {
        get => base.AutoSize;
        set => base.AutoSize = value;
    }

    [Browsable(false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public new event EventHandler? AutoSizeChanged
    {
        add => base.AutoSizeChanged += value;
        remove => base.AutoSizeChanged -= value;
    }

    /// <summary>
    ///  Gets or sets the page site
    /// </summary>
    protected IComponentEditorPageSite? PageSite { get; set; }

    /// <summary>
    ///  Gets or sets the component to edit
    /// </summary>
    protected IComponent? Component { get; set; }

    /// <summary>
    ///  Indicates whether the page is being activated for the first time
    /// </summary>
    protected bool FirstActivate { get; set; } = true;

    /// <summary>
    ///  Indicates whether a load is required previous to editing
    /// </summary>
    protected bool LoadRequired { get; set; }

    /// <summary>
    ///  Indicates if loading is taking place
    /// </summary>
    protected int Loading { get; set; }

    /// <summary>
    ///  Indicates whether an editor should apply its changes before it is deactivated
    /// </summary>
    public bool CommitOnDeactivate { get; set; }

    /// <summary>
    ///  Gets or sets the creation parameters for this control
    /// </summary>
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.Style &= ~(int)(WINDOW_STYLE.WS_BORDER | WINDOW_STYLE.WS_OVERLAPPED | WINDOW_STYLE.WS_DLGFRAME);
            return cp;
        }
    }

    /// <summary>
    ///  Gets or sets the icon for this page
    /// </summary>
    [AllowNull]
    public Icon Icon
    {
        get => _icon ??= new Icon(typeof(ComponentEditorPage), "ComponentEditorPage");
        set => _icon = value;
    }

    /// <summary>
    ///  Gets or sets the title of the page
    /// </summary>
    public virtual string Title => base.Text;

    /// <summary>
    ///  Activates and displays the page.
    /// </summary>
    public virtual void Activate()
    {
        if (LoadRequired)
        {
            EnterLoadingMode();
            LoadComponent();
            ExitLoadingMode();

            LoadRequired = false;
        }

        Visible = true;
        FirstActivate = false;
    }

    /// <summary>
    ///  Applies changes to all the components being edited
    /// </summary>
    public virtual void ApplyChanges() => SaveComponent();

    /// <summary>
    ///  Deactivates and hides the page
    /// </summary>
    public virtual void Deactivate()
    {
        Visible = false;
    }

    /// <summary>
    ///  Increments the loading counter, which determines whether a page is in loading mode.
    /// </summary>
    protected void EnterLoadingMode() => Loading++;

    /// <summary>
    ///  Decrements the loading counter, which determines whether a page is in loading mode.
    /// </summary>
    protected void ExitLoadingMode()
    {
        if (Loading == 0)
        {
            return;
        }

        Loading--;
    }

    /// <summary>
    ///  Gets the control that represents the window for this page
    /// </summary>
    public virtual Control GetControl() => this;

    /// <summary>
    ///  Gets the component that is to be edited
    /// </summary>
    protected IComponent? GetSelectedComponent() => Component;

    /// <summary>
    ///  Processes messages that could be handled by the page
    /// </summary>
    public virtual bool IsPageMessage(ref Message msg) => PreProcessMessage(ref msg);

    /// <summary>
    ///  Gets a value indicating whether the page is being activated for the first time
    /// </summary>
    protected bool IsFirstActivate() => FirstActivate;

    /// <summary>
    ///  Gets a value indicating whether the page is being loaded
    /// </summary>
    protected bool IsLoading() => Loading != 0;

    /// <summary>
    ///  Loads the component into the page UI
    /// </summary>
    protected abstract void LoadComponent();

    /// <summary>
    ///  Called when the page along with its sibling pages have applied their changes
    /// </summary>
    public virtual void OnApplyComplete() => ReloadComponent();

    /// <summary>
    ///  Called when the current component may have changed elsewhere and needs to be reloaded into the UI
    /// </summary>
    protected virtual void ReloadComponent()
    {
        if (!Visible)
        {
            LoadRequired = true;
        }
    }

    /// <summary>
    ///  Saves the component from the page UI
    /// </summary>
    protected abstract void SaveComponent();

    /// <summary>
    ///  Sets the page to be in dirty state
    /// </summary>
    protected virtual void SetDirty()
    {
        if (!IsLoading() && PageSite is not null)
        {
            PageSite.SetDirty();
        }
    }

    /// <summary>
    ///  Sets the component to be edited
    /// </summary>
    public virtual void SetComponent(IComponent? component)
    {
        Component = component;
        LoadRequired = true;
    }

    /// <summary>
    ///  Sets the site for this page.
    /// </summary>
    public virtual void SetSite(IComponentEditorPageSite? site)
    {
        PageSite = site;
        site?.GetControl()?.Controls.Add(this);
    }

    /// <summary>
    ///  Provides help information to the help system
    /// </summary>
    public virtual void ShowHelp()
    {
    }

    /// <summary>
    ///  Gets a value indicating whether the editor supports Help
    /// </summary>
    public virtual bool SupportsHelp() => false;
}
